/*****************************************************************
Copyright (c) 2011 - 2017 Qualcomm Technologies International, Ltd.

PROJECT
    source

FILE NAME
    source_power.c

DESCRIPTION
    Handles power readings when running as a self powered device.

*/


/* header for this file */
#include "source_power.h"
/* application header files */
#include "source_debug.h"
#include "source_states.h"
#include "source_app_msg_handler.h"
#include "source_memory.h"
#include "source_led_handler.h"
#include "source_buttons.h"
#include "headset_region_config.h"
#include "headset_soc_config.h"
#include "power_manager.h"
#include "power_manager_action.h"
#include "battery_region.h"
#include "state_of_charge.h"
#include "battery_monitor.h"
#include "charger_monitor.h"
#include "power_manager_conditions.h"
#include "power_manager.h"
/* VM headers */
#include <charger.h>
#include <panic.h>
#include <logging.h>
#include <psu.h>
#include <system_reboot.h>

#ifdef DEBUG_POWER
    #define POWER_DEBUG(x) SOURCE_DEBUG(x)
#else
    #define POWER_DEBUG(x)
#endif

#define POWER_MESSAGE_BATTERY_LOW 0

/* PSKey configurable sniff parameters */
typedef struct
{
    bool charging;
    bool battery_low_notifying;
} POWER_DATA_T;

POWER_DATA_T POWER_RUNDATA = {0};

#ifdef INCLUDE_POWER_READINGS

static const charge_region_t* charge_config_table;
static const charge_region_t* discharge_config_table;

void SourcenNotifyBatteryLow(void)
{
    if(Charger_IsConnected())
    {
        return;
    }

    DEBUG_LOG_INFO("SourcenNotifyBatteryLow");
    MessageSendLater(&theSource->powerTask, POWER_MESSAGE_BATTERY_LOW, 0, SEC_TO_MS(60));
    POWER_RUNDATA.battery_low_notifying = TRUE;
}

void SourceDisableBatteryLowNotify(void)
{
    DEBUG_LOG_INFO("SourceDisableBatteryLowNotify");
    MessageCancelAll(&theSource->powerTask, POWER_MESSAGE_BATTERY_LOW);
    POWER_RUNDATA.battery_low_notifying = FALSE;
}

/* handler when safety region is entered */
static void SourceSafetyHandler(uint8 old_region_enum, uint8 new_region_enum)
{
    UNUSED(old_region_enum);
    UNUSED(new_region_enum);

    if(!Charger_IsConnected() && appPowerNeedsToPowerOff())
    {
        if(states_get_state() != SOURCE_STATE_POWERED_OFF)
        {
            if(states_get_state() == SOURCE_STATE_INITIALISING)
            {
                states_set_state_to_power_off();

            }
            else
            {
                states_set_state(SOURCE_STATE_POWERED_OFF);
            }
        }
    }
}

static void SourceTransitionHandler(uint8 old_region_enum, uint8 new_region_enum)
{
    UNUSED(old_region_enum);

    if((!Charger_IsConnected()) && (states_get_state() != SOURCE_STATE_POWERED_OFF))
    {
        if(discharge_config_table[new_region_enum].region_type == CRITICAL_REGION)
        {
            if(!POWER_RUNDATA.battery_low_notifying)
            {
                SourcenNotifyBatteryLow();
            }
        }
        else
        {
            SourceDisableBatteryLowNotify();
        }
    }
}

/*! \brief battery region component various state handlers*/
const battery_region_handlers_t source_region_handlers =
{
    {SourceSafetyHandler},
    {NULL},
    {SourceTransitionHandler}
};

/*! \brief Utility function to Initialise SoC component   */
static bool sourceSoCInit(void)
{
    const soc_lookup_t* config_table;
    unsigned config_table_size;

    config_table = HeadsetSoC_GetConfigTable(&config_table_size);

    /* set voltage->percentage config table */
    Soc_SetConfigurationTable(config_table, config_table_size);

    Soc_Init();

    return TRUE;
}

/*! \brief Utility function to Initialise Battery Region component   */
static bool sourceBatteryRegionInit(void)
{
    unsigned config_table_size;

    charge_config_table = HeadsetRegion_GetChargeModeConfigTable(&config_table_size);

    /* set charge mode config table */
    BatteryRegion_SetChargeRegionConfigTable(CHARGE_MODE, charge_config_table, config_table_size);

    discharge_config_table = HeadsetRegion_GetDischargeModeConfigTable(&config_table_size);

    /* set discharge mode config table */
    BatteryRegion_SetChargeRegionConfigTable(DISCHARGE_MODE, discharge_config_table, config_table_size);

    /* set region state handler functions list */
    BatteryRegion_SetHandlerStructure(&source_region_handlers);

    BatteryRegion_Init();

    return TRUE;
}

static void power_msg_handler(Task task, MessageId id, Message message)
{
    UNUSED(task);
    UNUSED(message);
    DEBUG_LOG_INFO("power_msg_handler id = 0x%X", id);

    switch(id)
    {
        case MESSAGE_BATTERY_INIT_CFM:
        {
            DEBUG_LOG_INFO(" - MESSAGE_BATTERY_INIT_CFM");

            Charger_Init(NULL);

            sourceBatteryRegionInit();

            sourceSoCInit();

            /* register with charger monitor to receive charger messages. */
            Charger_ClientRegister(&theSource->powerTask);

            BatteryRegion_Register(&theSource->powerTask);

            soc_registration_form_t battery_registration_form;
            battery_registration_form.task = &theSource->powerTask;
            battery_registration_form.hysteresis = 1;
            Soc_Register((soc_registration_form_t *)&battery_registration_form);

            states_set_state(SOURCE_STATE_INITIALISING);
        }
        break;
        case CHARGER_MESSAGE_DETACHED:
        {
            DEBUG_LOG_INFO(" - CHARGER_MESSAGE_COMPLETED");
            Charger_RestoreState();

            if(states_get_state() == SOURCE_STATE_POWERED_OFF)
            {
                PioSet32Bank(0, PIO_LED_BATT_SWITCH, 0);

                MessageSendLater(app_get_instance(), APP_POWER_OFF_DEVICE, 0, 1000 * (3));
            }

            if(id == CHARGER_MESSAGE_DETACHED)
            {
                leds_show_event(LED_EVENT_CHARGER_DETECH);
            }
        }
        break;
        case CHARGER_MESSAGE_DISABLED:
        case CHARGER_MESSAGE_COMPLETED:
        {
            DEBUG_LOG_INFO(" - CHARGER_MESSAGE_COMPLETED");
            if(states_get_state() == SOURCE_STATE_POWERED_OFF)
            {
                MessageSendLater(app_get_instance(), APP_POWER_OFF_DEVICE, 0, 1000 *(3));
            }

            leds_show_event(LED_EVENT_CHARGER_DETECH);
        }
        break;
        case CHARGER_MESSAGE_ATTACHED:
        {
            DEBUG_LOG_INFO(" - CHARGER_MESSAGE_ATTACHED");
            SourceDisableBatteryLowNotify();

            if(states_get_state() != SOURCE_STATE_INITIALISING)
            {
                if(Charger_IsCharging())
                {
                    power_show_charging_led();
                }
                else
                {
                    leds_show_event(LED_EVENT_CHARGER_DETECH);
                }
            }
        }
        break;
        case CHARGER_MESSAGE_CHARGING_OK:
        {
            DEBUG_LOG_INFO(" - CHARGER_MESSAGE_CHARGING_OK");
        }
        break;
        case CHARGER_MESSAGE_CHARGING_LOW:
        {
            DEBUG_LOG_INFO(" - CHARGER_MESSAGE_CHARGING_LOW");
        }
        break;
        case MESSAGE_BATTERY_REGION_UPDATE:
        {
            DEBUG_LOG_INFO(" - MESSAGE_BATTERY_REGION_UPDATE");
            if(!Charger_IsConnected() && appPowerNeedsToPowerOff())
            {
                if(states_get_state() != SOURCE_STATE_POWERED_OFF)
                {
                    states_set_state(SOURCE_STATE_POWERED_OFF);
                }
            }
            else if(Charger_IsConnected())
            {
                if(Charger_IsCharging())
                {
                    power_show_charging_led();
                }
                else
                {
                    leds_show_event(LED_EVENT_CHARGER_DETECH);
                }
            }
        }
        break;
        case SOC_UPDATE_IND:
        {
            const MESSAGE_SOC_UPDATE_T *battery_level = message;

            if(Charger_IsConnected())
            {
                power_show_charging_led();

                if((battery_level->percent >= 95) && (states_get_state() == SOURCE_STATE_POWERED_OFF))
                {
                    leds_show_event(LED_EVENT_CHARGER_DETECH);

                    PioSet32Bank(0, PIO_LED_BATT_SWITCH, 0);

                    MessageSendLater(app_get_instance(), APP_POWER_OFF_DEVICE, 0, 1000 * (3));
                }
            }
        }
        break;
        case POWER_MESSAGE_BATTERY_LOW:
        {
            DEBUG_LOG_INFO(" - POWER_MESSAGE_BATTERY_LOW");
            leds_show_event(LED_EVENT_LOW_POWER);
            SourcenNotifyBatteryLow();
        }
        break;
        default:
        {
            DEBUG_LOG_INFO(" - Unhandled Power message 0x%x\n", id);
        }
        break;
    }
}


/***************************************************************************
Functions
****************************************************************************
*/

/****************************************************************************
NAME
    power_init - Initialises the power manager.

*/
void power_init(void)
{
    theSource->powerTask.handler = power_msg_handler;

    appBatteryInit(&theSource->powerTask);

    appBatterySetInitTask(&theSource->powerTask);
}

#endif /*INCLUDE_POWER_READINGS */
/****************************************************************************
NAME
    power_is_charger_connected -

DESCRIPTION
        This function checks  if the charger is connected  or not

RETURNS
    bool

*/
bool power_is_charging(void)
{
    context_t context = appChargerCurrentContext();

    if((context == context_charger_low) || (context == context_charger_fast))
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

void power_show_battery_level(void)
{
    uint8 soc = Soc_ConvertLevelToPercentage(appBatteryGetVoltageAverage());

    PioSet32Bank(0, PIO_LED_BATT_SWITCH, PIO_LED_BATT_SWITCH);

    if(soc <= 25)
    {
        leds_show_event(LED_EVENT_BATT_LVL_0);
    }
    else if((soc > 26) && (soc <= 50))
    {
        leds_show_event(LED_EVENT_BATT_LVL_1);
    }
    else if((soc > 51) && (soc <= 75))
    {
        leds_show_event(LED_EVENT_BATT_LVL_2);
    }
    else if(soc > 76)
    {
        leds_show_event(LED_EVENT_BATT_LVL_3);
    }
}

void power_show_charging_led(void)
{
    uint8 soc = Soc_ConvertLevelToPercentage(appBatteryGetVoltageAverage());
    PioSet32Bank(0, PIO_LED_BATT_SWITCH, PIO_LED_BATT_SWITCH);
    if(soc <= 25)
    {
        leds_show_event(LED_EVENT_CHARGING_LVL_0);
    }
    else if((soc > 26) && (soc <= 50))
    {
        leds_show_event(LED_EVENT_CHARGING_LVL_1);
    }
    else if((soc > 51) && (soc <= 75))
    {
        leds_show_event(LED_EVENT_CHARGING_LVL_2);
    }
    else if((soc > 76) && (soc <= 93))
    {
        leds_show_event(LED_EVENT_CHARGING_LVL_3);
    }
    else
    {
        leds_show_event(LED_EVENT_CHARGING_FULL);
    }
}

void power_show_battery_level_normal(void)
{
    uint8 soc = Soc_ConvertLevelToPercentage(appBatteryGetVoltageAverage());
    PioSet32Bank(0, PIO_LED_BATT_SWITCH, PIO_LED_BATT_SWITCH);
    if(soc <= 25)
    {
        leds_show_event(LED_EVENT_BATT_LVL_0_0);
    }
    else if((soc > 26) && (soc <= 50))
    {
        leds_show_event(LED_EVENT_BATT_LVL_1_1);
    }
    else if((soc > 51) && (soc <= 75))
    {
        leds_show_event(LED_EVENT_BATT_LVL_2_2);
    }
    else if(soc > 76)
    {
        leds_show_event(LED_EVENT_BATT_LVL_3_3);
    }
}
